package com.cn.delan.web.mvc.core.action;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import org.springframework.web.servlet.support.RequestContext;
import org.springframework.web.servlet.view.RedirectView;

import com.cn.delan.web.mvc.admin.AdminConst;
import com.cn.delan.web.mvc.admin.bean.UserBean;
import com.cn.delan.web.mvc.core.CoreConst;
import com.cn.delan.web.mvc.core.util.ParamUtil;
import com.cn.delan.web.mvc.core.util.ViewUtil;
/**
 * <b>Action抽象类</b><p/>
 * 实现了参数获取并填充到paramUtil中<br/>
 * 实现了将配置文件中的views配置填充到views中<br/>
 * @author BingGo
 * @version 1.0
 */
public abstract class AbstractAction implements Controller{

	/**
	 * 默认展示页
	 */
	public static final String INPUT = "input";
	/**
	 * 提交成功
	 */
	public static final String SUCCESS = "success";
	/**
	 * 提交失败
	 */
	public static final String FAIL = "fail";
	/**
	 * 重定向
	 */
	public static final String REDIRECT = "redirect";
	/**
	 * 用户管理
	 */
	public static final String USER = "user";
	/**
	 * 显示风格
	 */
	private String themes;
	/**
	 * 请求上下文,可以通过此对象获取国际化信息
	 */
	private RequestContext requestContext;
	/**
	 * 应用上下文
	 */
	private ServletContext servletContext;
	/**
	 * 请求对象的HttpSession
	 */
	private HttpSession session;
	private HttpServletRequest request;
	private HttpServletResponse response;
	/**
	 * 必须登录,当其为true时会先进行验证是否已经登录
	 */
	private boolean mustLogin;
	/**
	 * 参数工具类<br/>
	 * 此类存储了一次请求中所有request参数<br/>
	 * 支持单实例,在发生请求时进行填充<br/>
	 * @see ParamUtil
	 */
	private ParamUtil paramUtil;
	/**
	 * 视图工具类<br/>
	 * 此类存储了配置文件中的views,用于处理完业务return<br/>
	 */
	private ViewUtil views;
	/**
	 * 继承的请求处理方法
	 * @see Controller
	 */
	@Override
	public ModelAndView handleRequest(HttpServletRequest request,HttpServletResponse response) throws Exception {
		return this.service(request, response);
	}
	/**
	 * 同步的请求处理方法,确保单实例模式能够正常运行
	 * @param request http请求对象
	 * @param response http响应对象
	 * @return 模型和视图
	 * @throws Exception
	 */
	synchronized private ModelAndView service(HttpServletRequest request,HttpServletResponse response) throws Exception {
		this.requestContext = new RequestContext(request);
		this.request = request;
		this.response = response;
		this.session = request.getSession();
		this.servletContext = request.getServletContext();
		this.servletContext.setAttribute(CoreConst.CONTEXT_ATTR_PATH, this.servletContext.getContextPath());
		//登录验证
		if(this.isMustLogin()){
			if(!this.isLogin(this.session)){
				request.setAttribute(AdminConst.REQUEST_ATTR_MESSAGE, this.getRequestContext().getMessage(CoreConst.MESSAGE_NOTLOGIN));
				request.getRequestDispatcher("login.html").forward(request, response);
				return null;
			}
		}
		//请求参数填充
		this.fillParam(request);
		//显示风格
		if(this.getParameter(CoreConst.REQUEST_PARA_THEMES)!=null){
			this.themes = this.getParameter(CoreConst.REQUEST_PARA_THEMES);
		}else{
			this.themes = (String)this.session.getAttribute(CoreConst.SESSION_ATTR_THEMES);
		}
		if(this.themes==null){
			this.themes = CoreConst.DEFAULT_THEMES;
		}
		this.session.setAttribute(CoreConst.SESSION_ATTR_THEMES, this.themes);
		//真实的请求处理
		return this.handle(request,response);
	}
	/**
	 * 判断当前会话是否已经登录
	 * @param session 会话对象
	 * @return 如登录返回true,否则为false
	 */
	public boolean isLogin(HttpSession session){
		return session.getAttribute(CoreConst.SESSION_ATTR_USER)!=null;
	}
	/**
	 * 用户进行登录标记
	 * @param session 会话对象
	 * @param user 登录的用户
	 */
	public void login(HttpSession session,UserBean user){
		session.setAttribute(CoreConst.SESSION_ATTR_USER, user);
	}
	/**
	 * 用户注销
	 * @param session 当前会话对象
	 */
	public void logout(HttpSession session){
		session.removeAttribute(CoreConst.SESSION_ATTR_USER);
	}
	/**
	 * Json消息生成方法
	 * @param success 成功状态
	 * @param message 消息体
	 * @return Json的字符串形式
	 */
	public String toJson(boolean success,String message){
		return "{'success':"+success+",'message':'"+message+"'}";
	}
	/**
	 * 直接将Json输出到响应流中
	 * @param response 响应对象
	 * @param success 成功状态
	 * @param message 消息体
	 * @throws IOException
	 */
	public void outJson(HttpServletResponse response,boolean success,String message) throws IOException{
		PrintWriter out = response.getWriter();
		out.print(this.toJson(success, message));
	}
	/**
	 * 填充请求参数到参数工具类
	 * @param request http请求对象
	 */
	private void fillParam(HttpServletRequest request){
		//实例化参数工具类,单实例时参数工具类同样使用单实例以节省系统资源
		if(this.getParamUtil()==null){
			this.paramUtil = new ParamUtil();
		}
		//清空参数工具类
		this.paramUtil.clear();
		Enumeration<String> e = request.getParameterNames();
		//遍历并填充请求参数
		while(e.hasMoreElements()){
			String name = e.nextElement();
			this.paramUtil.put(name, request.getParameterValues(name));
		}
	}
	/**
	 * 真实的请求处理方法,不同的action请求相应要重写此方法以实现不同的业务逻辑
	 * @param request http请求对象
	 * @param response http响应对象
	 * @return 模型和视图
	 * @throws Exception
	 */
	public abstract ModelAndView handle(HttpServletRequest request,HttpServletResponse response) throws Exception;
	/**
	 * 根据参数名称获取参数值数组,相当于request.getParameterValues()方法<br/>
	 * 不同的是这里的从paramUtil中获取,paramUtil中的中文参数经过编码处理
	 * @param parameterName 参数名称
	 * @return 返回指定的参数值数组
	 */
	public String[] getParameterValues(String parameterName){
		return this.paramUtil.get(parameterName);
	}
	/**
	 * 根据参数名称获取参数值,相当于request.getParameter()方法<br/>
	 * 不同的是这里的从paramUtil中获取,paramUtil中的中文参数经过编码处理
	 * @param parameterName 参数名称
	 * @return 返回指定的参数值
	 */
	public String getParameter(String parameterName){
		String values[] = this.paramUtil.get(parameterName);
		return (values==null||values.length==0)?null:values[0];
	}
	/**
	 * 根据视图名称获取视图对象,当配置文件中无法找到对应的视图名称时返回null
	 * @param viewName 视图名称
	 * @return 视图对象,当配置文件中无法找到对应的视图名称时返回null
	 */
	public ModelAndView getView(String viewName){
		String viewPath = this.views.get(viewName);
		return viewPath==null?null:new ModelAndView(viewPath);
	}
	/**
	 * 根据url获取视图对象
	 * @param url 视图地址 WEB-INF/views下的路径如：/admin/login.jsp
	 * @return 视图对象
	 */
	public ModelAndView getViewByUrl(String url){
		return url==null?null:new ModelAndView(url);
	}
	/**
	 * 根据视图名称获取重定向视图对象,当配置文件中无法找到对应的视图名称时返回null,,此种转发方式不能够保留请求参数
	 * @param viewName 视图名称
	 * @return 重定向视图对象,当配置文件中无法找到对应的视图名称时返回null
	 */
	public ModelAndView getRedirectView(String viewName){
		String viewPath = this.views.get(viewName);
		return viewPath==null?null:new ModelAndView(new RedirectView(viewPath));
	}
	/**
	 * 根据url获取重定向视图对象,此种转发方式不能够保留请求参数
	 * @param url 重定向的地址
	 * @return 重定向视图对象
	 */
	public ModelAndView getRedirectViewByUrl(String url){
		return url==null?null:new ModelAndView(new RedirectView(url));
	}
	/**
	 * 根据视图名称以forward方式转发请求,此种转发方式能够保留请求参数
	 * @param viewName 视图名称
	 * @return null
	 * @throws ServletException
	 * @throws IOException
	 */
	public ModelAndView getForwardView(String viewName) throws ServletException, IOException{
		String viewPath = this.views.get(viewName);
		this.request.getRequestDispatcher(viewPath).forward(this.request, this.response);
		return null;
	}
	/**
	 * 根据url地址以forward方式转发请求,此种转发方式能够保留请求参数
	 * @param url 转发地址
	 * @return null
	 * @throws ServletException
	 * @throws IOException
	 */
	public ModelAndView getForwardViewByUrl(String url) throws ServletException, IOException{
		this.request.getRequestDispatcher(url).forward(this.request, this.response);
		return null;
	}
	/**
	 * 校验验证码是否正确
	 * @param session 当前会话
	 * @return true则通过校验，否则为false
	 */
	public boolean checkRandCode(HttpSession session){
		String randCode = this.getParameter(CoreConst.REQUEST_PARA_RANDCODE);
		String validcode = (String)session.getAttribute(CoreConst.SESSION_ATTR_VALIDCODE);
		return randCode!=null && validcode!=null && randCode.toLowerCase().equals(validcode);
	}
	/**
	 * 获取国际化信息
	 * @param i18nCode 国际化编码
	 * @return 返回国际化信息
	 */
	public String getMessage(String i18nCode){
		return this.getRequestContext().getMessage(i18nCode);
	}
	/**
	 * 对message消息进行国际化处理,{code}中的内容进行国际化替换
	 * @param message 国际化前的消息
	 * @return 国际化后的消息
	 */
	public String getFormatMessage(String message){
		String formatMessage = message;
		String regex = "\\{([^\\}]*)\\}";
		Pattern pattern = Pattern.compile(regex);
		Matcher matcher = pattern.matcher(message);
		while(matcher.find()){
			String code = matcher.group(1);
			formatMessage = formatMessage.replaceAll("\\{"+code+"\\}", this.getMessage(code));
		}
		return formatMessage;
	}
	/**
	 * 获取输出对象
	 */
	public PrintWriter getWriter() throws IOException{
		return this.response.getWriter();
	}
	/**
	 * 控制台输出当前获取到的参数列表
	 */
	public void printParams(){
		ParamUtil util = this.getParamUtil();
		for(String key:util.keySet()){
			System.out.println("key:"+key);
			for(String param:util.get(key)){
				System.out.println(key+"["+param+"]");
			}
			System.out.println("-----------------------");
		}
		System.out.println("=========================");
	}
	public static void main(String []args){
		String formatMessage = "{admin.sysman.sucdel}1{admin.sysman.filecount}";
		String regex = "\\{([^\\}]*)\\}";
		Pattern pattern = Pattern.compile(regex);
		Matcher matcher = pattern.matcher(formatMessage);
		while(matcher.find()){
			int count = matcher.groupCount();
			for(int i=1;i<=count;i++)
				System.out.println(matcher.group(i));
		}
	}
	public String getThemes() {
		return themes;
	}
	public void setThemes(String themes) {
		this.themes = themes;
	}
	public RequestContext getRequestContext() {
		return requestContext;
	}
	public void setRequestContext(RequestContext context) {
		this.requestContext = context;
	}
	public ServletContext getServletContext() {
		return servletContext;
	}
	public void setServletContext(ServletContext servletContext) {
		this.servletContext = servletContext;
	}
	public HttpSession getSession() {
		return session;
	}
	public void setSession(HttpSession session) {
		this.session = session;
	}
	public HttpServletRequest getRequest() {
		return request;
	}
	public void setRequest(HttpServletRequest request) {
		this.request = request;
	}
	public HttpServletResponse getResponse() {
		return response;
	}
	public void setResponse(HttpServletResponse response) {
		this.response = response;
	}
	public boolean isMustLogin() {
		return mustLogin;
	}
	public void setMustLogin(boolean mustLogin) {
		this.mustLogin = mustLogin;
	}
	public ParamUtil getParamUtil() {
		return paramUtil;
	}
	public void setParamUtil(ParamUtil paramUtil) {
		this.paramUtil = paramUtil;
	}
	public ViewUtil getViews() {
		return views;
	}
	public void setViews(ViewUtil views) {
		this.views = views;
	}

}
